/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.tomcat.util.modeler;

import java.util.ArrayList;
import java.util.Iterator;

import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;

/**
 * <p>
 * Implementation of <code>NotificationBroadcaster</code> for attribute change
 * notifications. This class is used by <code>BaseModelMBean</code> to handle
 * notifications of attribute change events to interested listeners.
 *</p>
 * 
 * @author Craig R. McClanahan
 * @author Costin Manolache
 */

public class BaseNotificationBroadcaster implements NotificationBroadcaster {

	// ----------------------------------------------------------- Constructors

	// ----------------------------------------------------- Instance Variables

	/**
	 * The set of registered <code>BaseNotificationBroadcasterEntry</code>
	 * entries.
	 */
	protected ArrayList entries = new ArrayList();

	// --------------------------------------------------------- Public Methods

	/**
	 * Add a notification event listener to this MBean.
	 * 
	 * @param listener
	 *            Listener that will receive event notifications
	 * @param filter
	 *            Filter object used to filter event notifications actually
	 *            delivered, or <code>null</code> for no filtering
	 * @param handback
	 *            Handback object to be sent along with event notifications
	 * 
	 * @exception IllegalArgumentException
	 *                if the listener parameter is null
	 */
	public void addNotificationListener(NotificationListener listener,
			NotificationFilter filter, Object handback)
			throws IllegalArgumentException {

		synchronized (entries) {

			// Optimization to coalesce attribute name filters
			if (filter instanceof BaseAttributeFilter) {
				BaseAttributeFilter newFilter = (BaseAttributeFilter) filter;
				Iterator items = entries.iterator();
				while (items.hasNext()) {
					BaseNotificationBroadcasterEntry item = (BaseNotificationBroadcasterEntry) items
							.next();
					if ((item.listener == listener) && (item.filter != null)
							&& (item.filter instanceof BaseAttributeFilter)
							&& (item.handback == handback)) {
						BaseAttributeFilter oldFilter = (BaseAttributeFilter) item.filter;
						String newNames[] = newFilter.getNames();
						String oldNames[] = oldFilter.getNames();
						if (newNames.length == 0) {
							oldFilter.clear();
						} else {
							if (oldNames.length != 0) {
								for (int i = 0; i < newNames.length; i++)
									oldFilter.addAttribute(newNames[i]);
							}
						}
						return;
					}
				}
			}

			// General purpose addition of a new entry
			entries.add(new BaseNotificationBroadcasterEntry(listener, filter,
					handback));
		}

	}

	/**
	 * Return an <code>MBeanNotificationInfo</code> object describing the
	 * notifications sent by this MBean.
	 */
	public MBeanNotificationInfo[] getNotificationInfo() {

		return (new MBeanNotificationInfo[0]);

	}

	/**
	 * Remove a notification event listener from this MBean.
	 * 
	 * @param listener
	 *            The listener to be removed (any and all registrations for this
	 *            listener will be eliminated)
	 * 
	 * @exception ListenerNotFoundException
	 *                if this listener is not registered in the MBean
	 */
	public void removeNotificationListener(NotificationListener listener)
			throws ListenerNotFoundException {

		synchronized (entries) {
			Iterator items = entries.iterator();
			while (items.hasNext()) {
				BaseNotificationBroadcasterEntry item = (BaseNotificationBroadcasterEntry) items
						.next();
				if (item.listener == listener)
					items.remove();
			}
		}

	}

	/**
	 * Remove a notification event listener from this MBean.
	 * 
	 * @param listener
	 *            The listener to be removed (any and all registrations for this
	 *            listener will be eliminated)
	 * @param handback
	 *            Handback object to be sent along with event notifications
	 * 
	 * @exception ListenerNotFoundException
	 *                if this listener is not registered in the MBean
	 */
	public void removeNotificationListener(NotificationListener listener,
			Object handback) throws ListenerNotFoundException {

		removeNotificationListener(listener);

	}

	/**
	 * Remove a notification event listener from this MBean.
	 * 
	 * @param listener
	 *            The listener to be removed (any and all registrations for this
	 *            listener will be eliminated)
	 * @param filter
	 *            Filter object used to filter event notifications actually
	 *            delivered, or <code>null</code> for no filtering
	 * @param handback
	 *            Handback object to be sent along with event notifications
	 * 
	 * @exception ListenerNotFoundException
	 *                if this listener is not registered in the MBean
	 */
	public void removeNotificationListener(NotificationListener listener,
			NotificationFilter filter, Object handback)
			throws ListenerNotFoundException {

		removeNotificationListener(listener);

	}

	/**
	 * Send the specified notification to all interested listeners.
	 * 
	 * @param notification
	 *            The notification to be sent
	 */
	public void sendNotification(Notification notification) {

		synchronized (entries) {
			Iterator items = entries.iterator();
			while (items.hasNext()) {
				BaseNotificationBroadcasterEntry item = (BaseNotificationBroadcasterEntry) items
						.next();
				if ((item.filter != null)
						&& (!item.filter.isNotificationEnabled(notification)))
					continue;
				item.listener.handleNotification(notification, item.handback);
			}
		}

	}

	// -------------------- Internal Extensions --------------------

	// Fast access. First index is the hook type
	// ( FixedNotificationFilter.getType() ).
	NotificationListener hooks[][] = new NotificationListener[20][];
	int hookCount[] = new int[20];

	private synchronized void registerNotifications(
			FixedNotificationFilter filter) {
		String names[] = filter.getNames();
		Registry reg = Registry.getRegistry();
		for (int i = 0; i < names.length; i++) {
			int code = reg.getId(null, names[i]);
			if (hooks.length < code) {
				// XXX reallocate
				throw new RuntimeException("Too many hooks " + code);
			}
			NotificationListener listeners[] = hooks[code];
			if (listeners == null) {

			}

		}
	}

}

/**
 * Utility class representing a particular registered listener entry.
 */

class BaseNotificationBroadcasterEntry {

	public BaseNotificationBroadcasterEntry(NotificationListener listener,
			NotificationFilter filter, Object handback) {
		this.listener = listener;
		this.filter = filter;
		this.handback = handback;
	}

	public NotificationFilter filter = null;

	public Object handback = null;

	public NotificationListener listener = null;

}
